package com.majun.file;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.HttpMethod;
import com.amazonaws.SdkClientException;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.services.s3.transfer.*;
import com.amazonaws.services.s3.transfer.model.UploadResult;
import com.amazonaws.util.StringUtils;
import com.majun.config.S3Config;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

import static java.lang.System.in;

@Component
public class AwzS3Util {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private AmazonS3 amazonS3;

    //或
    @Autowired
    private TransferManager transferManager;

    @Autowired
    private S3Config s3Config;


//    @PostConstruct
//    public void init() {
//        awzS3ProtiesConfig = this.injectAwzS3ProtiesConfig;
//        AWSCredentials credentials = new BasicAWSCredentials(awzS3ProtiesConfig.getAccessKey(), awzS3ProtiesConfig.getSecretKey());
//        AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);
//        AwsClientBuilder.EndpointConfiguration endpointConfig =
//                new AwsClientBuilder.EndpointConfiguration(awzS3ProtiesConfig.getUrl(), Regions.CN_NORTH_1.getName());
//        ClientConfiguration config = new ClientConfiguration();
//        config.setSignerOverride("S3SignerType");
//        config.setProtocol(Protocol.HTTP);
//        config.withUseExpectContinue(false);
//        config.disableSocketProxy();
//        amazonS3 = AmazonS3Client.builder()
//                .withEndpointConfiguration(endpointConfig)
//                .withClientConfiguration(config)
//                .withCredentials(credentialsProvider)
//                .disableChunkedEncoding()
//                .withPathStyleAccessEnabled(true)
//                .withForceGlobalBucketAccessEnabled(true)
//                .build();
//    }

    /**
     * 创建桶
     *
     * @param bucket
     * @return boolean
     */
    public boolean createBucket(String bucket) {
        if (StringUtils.isNullOrEmpty(bucket)) {
            throw new IllegalArgumentException("桶名称不能为空!");
        }
        try {
            amazonS3.createBucket(bucket);
        } catch (SdkClientException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除桶
     *
     * @param bucket
     * @return boolean
     */
    public boolean deleteBucket(String bucket) {
        if (StringUtils.isNullOrEmpty(bucket)) {
            throw new IllegalArgumentException("桶名称不能为空!");
        }
        try {
            amazonS3.deleteBucket(bucket);
        } catch (SdkClientException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 上传文件
     *
     * @param bucket 桶名称
     * @param file
     * @return boolean
     */
    public boolean uploadLocal(String bucket, File file) {

        if (Objects.isNull(file)) {
            throw new IllegalArgumentException("文件不能为空!");
        }
        String fileName = file.getName();
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType("sql");
        objectMetadata.setContentLength(file.length());
        bucket = getBucket(bucket);
        try {
            PutObjectRequest request = new PutObjectRequest(bucket, fileName, new FileInputStream(file), objectMetadata)
                    // 设置文件访问权限
                    .withCannedAcl(CannedAccessControlList.BucketOwnerFullControl);
            PutObjectResult result = amazonS3.putObject(request);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 上传文件
     *
     * @param file
     * @param bucket 桶名称
     * @return boolean
     */
    public boolean upload(MultipartFile file, String bucket) {
        if (Objects.isNull(file)) {
            throw new IllegalArgumentException("文件不能为空!");
        }
        String fileName = file.getOriginalFilename();
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(file.getContentType());
        objectMetadata.setContentLength(file.getSize());
        bucket = getBucket(bucket);
        try {
            PutObjectRequest request = new PutObjectRequest(bucket, fileName, file.getInputStream(), objectMetadata)
                    // 设置文件访问权限
//                    .withCannedAcl(CannedAccessControlList.BucketOwnerFullControl);
                    .withCannedAcl(CannedAccessControlList.PublicRead);
            PutObjectResult result = amazonS3.putObject(request);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 通过文件名称下载
     *
     * @param bucket
     * @param fileName
     * @param response
     * @throws IOException
     */
    public void downloadByName(String bucket, String fileName, HttpServletResponse response) throws IOException {
        if (StringUtils.isNullOrEmpty(bucket)) {
            throw new IllegalArgumentException("文件名称不能为空!");
        }
        bucket = getBucket(bucket);
        GetObjectRequest getObjectRequest = new GetObjectRequest(bucket, fileName);
        S3Object s3Object = amazonS3.getObject(getObjectRequest);
        response.reset();
        response.setContentType(MediaType.TEXT_PLAIN_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        String encodeName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
//        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
//        HttpHeaders headers = new HttpHeaders();
//        headers.setContentType(MediaType.parseMediaType(contentType));
//        headers.setContentDisposition(ContentDisposition.builder("attachment").filename(encodeName).build());
//        ContentDisposition contentDisposition = ContentDisposition.builder("attachment").filename(encodeName).build();
        ContentDisposition contentDisposition = ContentDisposition.attachment().filename(encodeName).build();

        System.err.println(contentDisposition);
        System.err.println(contentDisposition.toString());
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
        try (S3ObjectInputStream objectInputStream = s3Object.getObjectContent();
             ServletOutputStream outputStream = response.getOutputStream();) {
//            byte[] buf = new byte[1024 * 10];
//            int readLength;
//            while (((readLength = objectInputStream.read(buf)) != -1)) {
//                outputStream.write(buf, 0, readLength);
//            }
//            outputStream.flush();
            // 使用工具类
            IOUtils.copy(objectInputStream, outputStream);
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
        }
    }

    /**
     * @param response
     * @param attachment
     */
    public void writeFileToResp(HttpServletResponse response, byte[] attachment) {
        try (ServletOutputStream outputStream = response.getOutputStream();) {
//            byte[] buf = new byte[1024 * 10];
//            int readLength;
//            while (((readLength = inputStream.read(buf)) != -1)) {
//                outputStream.write(buf, 0, readLength);
//            }
            outputStream.write(attachment);
            outputStream.flush();
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
        }
    }

    /**
     * @param bucket
     * @return {@link String }
     */
    private String getBucket(String bucket) {
        String bucketString = StringUtils.isNullOrEmpty(bucket) ? s3Config.getBucketName() : bucket;
        return bucketString;
    }

    /**
     * @param bucket
     * @param fileName
     * @return {@link String }
     *///生成私有对象可下载的URL链接
    //AWS Java SDK支持生成可下载私有对象的URL连接，您可以将该链接提供给第三方进行文件下载：
    public String generatePresignedUrl(String bucket, String fileName) {
        GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucket, fileName);
        // 设置可下载URL的过期时间为1天后
        generatePresignedUrlRequest.setExpiration(new Date(System.currentTimeMillis() + 10 * 60 * 1000));
        generatePresignedUrlRequest.withMethod(HttpMethod.GET);
        URL url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest);//生成URL
        System.out.println(url);//可以用这个url来下载文件
        return url.toString();
    }

    //AWS Java SDK支持生成可下载私有对象的URL连接，您可以将该链接提供给第三方进行文件下载：
    public String getUrl(String bucket, String fileName) {
        //生成URL
        URL url = amazonS3.getUrl(bucket, fileName);
        System.out.println(url);
        return url.toString();
    }

    /**
     * 查找桶内文件是否存在
     *
     * @param fileKey
     * @return
     */
    public boolean exist(String bucket, String fileKey) {
        //获取客户端连接
        boolean b = amazonS3.doesObjectExist(bucket, fileKey);
        return b;
    }

    /**
     * 通过文件名称下载
     *
     * @param bucket
     * @param fileName
     * @return {@link byte[] }
     * @throws IOException
     */
    public byte[] download(String bucket, String fileName) throws IOException {
        if (StringUtils.isNullOrEmpty(bucket)) {
            throw new IllegalArgumentException("文件名称不能为空!");
        }
        GetObjectRequest getObjectRequest = new GetObjectRequest(bucket, fileName);
        S3Object s3Object = amazonS3.getObject(getObjectRequest);
        byte[] byteArray = null;
        try (S3ObjectInputStream objectInputStream = s3Object.getObjectContent();) {
            // 使用工具类
            byteArray = com.amazonaws.util.IOUtils.toByteArray(objectInputStream);
        } catch (Exception e) {
        }
        return byteArray;
    }

    /**
     * @param response
     * @param attachment
     * @throws UnsupportedEncodingException
     */
    public void writeBytesToResponse(HttpServletResponse response, byte[] attachment) throws UnsupportedEncodingException {
        response.reset();
        response.setContentType(MediaType.TEXT_PLAIN_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        String encodeFilename = URLEncoder.encode("345.sql", StandardCharsets.UTF_8.name());
        response.setHeader("Content-Disposition", "attachment;filename=" + encodeFilename);
        try (ServletOutputStream outputStream = response.getOutputStream();) {
            outputStream.write(attachment);
            outputStream.flush();
        } catch (Exception e) {
        }
    }

    /**
     * 删除单个文件
     *
     * @param bucket
     * @param fileName
     * @return
     */
    public boolean deleteFile(String bucket, String fileName) {
        if (StringUtils.isNullOrEmpty(fileName)) {
            throw new IllegalArgumentException("文件名称不能为空!");
        }
        try {
            amazonS3.deleteObject(bucket, fileName);
        } catch (SdkClientException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 获取文件信息
     *
     * @param bucket
     * @param fileName
     * @return
     */
    public ObjectMetadata getFileInfo(String bucket, String fileName) {
        if (StringUtils.isNullOrEmpty(fileName)) {
            throw new IllegalArgumentException("文件名称不能为空!");
        }
        ObjectMetadata objectMetadata = null;
        try {
            objectMetadata = amazonS3.getObjectMetadata(bucket, fileName);
        } catch (SdkClientException e) {
            e.printStackTrace();
        }
        return objectMetadata;
    }

    /**
     * 复制文件
     *
     * @param oldFileKey 原全路径
     * @param newFileKey 新全路径
     * @return boolean
     */
    public boolean copyFile(String bucketName, String oldFileKey, String newFileKey) {
        try {
            //复制文件
            CopyObjectRequest copyObjectRequest = new CopyObjectRequest(bucketName, oldFileKey, bucketName, newFileKey);
            CopyObjectResult copyObjectResult = amazonS3.copyObject(copyObjectRequest);
            //删除原文件
        } catch (Exception e) {
            logger.error(e.getMessage());
            return false;
        }
        return true;
    }

    /**
     * 移动文件（删除原文件）
     *
     * @param oldFileKey 原全路径
     * @param newFileKey 新全路径
     * @return boolean
     */
    public boolean mvFile(String bucketName, String oldFileKey, String newFileKey) {
        try {
            //复制文件
            CopyObjectRequest copyObjectRequest = new CopyObjectRequest(bucketName, oldFileKey, bucketName, newFileKey);
            amazonS3.copyObject(copyObjectRequest);
            //删除原文件
            amazonS3.deleteObject(new DeleteObjectRequest(bucketName, oldFileKey));
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    //////////////////////////////////////


    //1. 直接内容上传
    public void putObjectContent() {
        //要上传文件内容
        String content = "Object content";
        try {
            amazonS3.putObject("your-bucketname", "your-objectname", content);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

    }

    // 2. 本地文件普通上传
    //对于小对象可以使用putObject接口进行上传，putObject上传支持的最大文件大小为100M，如果上传大于100M的文件需要使用分块上传。本地文件普通上传的示例代码如下：
    public void putObject() {
        //要上传文件的路径
        String filePath = "your-local-file-path";
        try {
            amazonS3.putObject("your-bucketname", "your-objectname", new File(filePath));
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

    }

    //3. 上传文件时设置文件元数据信息
    public void putObjectMeta() {
        String filePath = "your-local-file-path";
        ObjectMetadata objectMetadata = new ObjectMetadata();
        //设置Content-Type
        objectMetadata.setContentType("application/xml");
        //设置标准http消息头（元数据）
        objectMetadata.setHeader("Cache-Control", "no-cache");
        //设置用户自定义元数据信息
        Map<String, String> userMeta = new HashMap<String, String>();
        userMeta.put("ud", "test");
        objectMetadata.setUserMetadata(userMeta);
        PutObjectRequest putObjectRequest = new PutObjectRequest("your-bucketname", "your-objectname", new File(filePath));
        putObjectRequest.setMetadata(objectMetadata);
        amazonS3.putObject(putObjectRequest);

    }

    //4. 流式上传
    public void upload03() {
        try {
            ObjectMetadata objectMetadata = new ObjectMetadata();
            //设置流的长度，您还可以设置其他文件元数据信息
            long streamLength = 0;
            objectMetadata.setContentLength(streamLength);
            InputStream inputStream = null;
            amazonS3.putObject("your-bucketname", "your-objectname", inputStream, objectMetadata);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    //1. 流式下载
    public void getObjectContent() {
        S3Object fileObject = amazonS3.getObject("your-bucketname", "your-objectname");
//可以通过getObjectMetadata方法获取对象的ContentType等元数据信息
        String contentType = fileObject.getObjectMetadata().getContentType();
//流式获取文件内容
        InputStream in = fileObject.getObjectContent();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        while (true) {
            String line;
            try {
                line = reader.readLine();
                if (line == null) break;
                System.out.println("\n" + line);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //2. 下载到本地文件
    public void getObject() {
        String destinationFile = "your-local-filepath";
        GetObjectRequest getObjectRequest = new GetObjectRequest("your-bucketname", "your-objectname");
        ObjectMetadata objectMetadata = amazonS3.getObject(getObjectRequest, new File(destinationFile));
    }

    //3. Range 下载
    public void getObjectRange() {
        GetObjectRequest getObjectRequest = new GetObjectRequest("your-bucketname", "your-objectname");
        getObjectRequest.setRange(0, 100);
        S3Object nosObject = amazonS3.getObject(getObjectRequest);
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        while (true) {
            String line;
            try {
                line = reader.readLine();
                if (line == null) break;
                System.out.println("\n" + line);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    //判断文件是否存在
    //您可以通过AmazonS3.doesObjectExist判断文件是否存在。
    public void doesObjectExist() {
        boolean isExist = amazonS3.doesObjectExist("your-bucketname", "your-objectname");
    }

    //文件删除
    //您可以通过AmazonS3.deleteObject删除单个文件
    public void deleteObject() {
        amazonS3.deleteObject("your-bucketname", "your-objectname");
    }

    public void deleteObject02() {
        amazonS3.deleteObject("your-bucketname", "your-objectname");

        DeleteObjectsRequest deleteObjectsRequest = null;

        try {
            DeleteObjectsResult result = amazonS3.deleteObjects(deleteObjectsRequest);
            List<DeleteObjectsResult.DeletedObject> deleteObjects = result.getDeletedObjects();
            //print the delete results
            for (DeleteObjectsResult.DeletedObject items : deleteObjects) {
                System.out.println(items.getKey());
            }
// 部分对象删除失败
        } catch (MultiObjectDeleteException e) {
            List<MultiObjectDeleteException.DeleteError> deleteErrors = e.getErrors();
            for (MultiObjectDeleteException.DeleteError error : deleteErrors) {
                System.out.println(error.getKey());
            }
        } catch (AmazonServiceException e) {
            //捕捉服务器异常错误
        } catch (AmazonClientException ace) {
            //捕捉客户端错误
        }
    }

    //获取文件元数据信息
    //您可以通过AmazonS3.getObjectMetadata获取文件元数据信息
    public void getObjectMetadata() {
        amazonS3.getObjectMetadata("your-bucketname", "your-objectname");
    }

    //文件复制(copy)
    //您可以通过AmazonS3.copyObject接口实现文件拷贝功能。
    public void copyObject() {
        amazonS3.copyObject("source-bucket", "source-object", "dst-bucket", "dst-object");
    }

    // 简单列举
    //简单列举只需指定需要列举的桶名，最多返回100条对象记录，建议桶内对象数较少时（小于100）使用。
    public void listObjectsSimple() {
        ObjectListing objectListing = amazonS3.listObjects("your-bucketname");
        List<S3ObjectSummary> sums = objectListing.getObjectSummaries();
        for (S3ObjectSummary s : sums) {
            System.out.println("\t" + s.getKey());
        }
    }

    //1、分页列举桶内的所有文件：
    public void listObjects() {

        List<S3ObjectSummary> listResult = new ArrayList<S3ObjectSummary>();
        ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
        listObjectsRequest.setBucketName("your-bucketname");
        listObjectsRequest.setMaxKeys(50);
        ObjectListing listObjects = amazonS3.listObjects(listObjectsRequest);
        do {
            listResult.addAll(listObjects.getObjectSummaries());
            if (listObjects.isTruncated()) {
                ListObjectsRequest request = new ListObjectsRequest();
                request.setBucketName(listObjectsRequest.getBucketName());
                request.setMarker(listObjects.getNextMarker());
                listObjects = amazonS3.listObjects(request);
            } else {
                break;
            }
        } while (listObjects != null);
    }

    //2、使用Delimiter模拟文件夹功能
    //
    //假设桶内有如下对象：a/1.jpg、a/2.jpg、a/b/1.txt、a/b/2.txt，列举a文件夹下的文件及子文件夹的示例代码如下：
    public void listDelimiter() {

        ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
        listObjectsRequest.setBucketName("your-bucketname");
        listObjectsRequest.setDelimiter("/");
        listObjectsRequest.setPrefix("a/");
        ObjectListing listing = amazonS3.listObjects(listObjectsRequest);
        // 遍历所有Object
        System.out.println("Objects:");
        for (S3ObjectSummary objectSummary : listing.getObjectSummaries()) {
            System.out.println(objectSummary.getKey());
        }
        // 遍历所有CommonPrefix
        System.out.println("CommonPrefixs:");
        for (String commonPrefix : listing.getCommonPrefixes()) {
            System.out.println(commonPrefix);
        }


    }

    //生成私有对象可下载的URL链接
    //AWS Java SDK支持生成可下载私有对象的URL连接，您可以将该链接提供给第三方进行文件下载：
    public void generatePresignedUrl() {

        String bucketName = "";
        String key = "";
        GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, key);
// 设置可下载URL的过期时间为1天后
        generatePresignedUrlRequest.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000 * 24));
        URL url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest);//生成URL
        System.out.println(url);//可以用这个url来下载文件


    }


    //////////////////////////////////////

    /**
     * 如果指定上传的本地文件大于16M，TransferManager会自动对文件进行分块，并发调用分块上传接口进行上传，大大提高上传文件的速度。
     *
     * @return void
     */
    public void upload() {
        //上传文件
        Upload upload = transferManager.upload("your-bucketname", "your-objectname", new File("your-file"));
        try {
            upload.waitForUploadResult();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }


    /**
     * 您也可以使用TransferManager进行流式上传，但是相比本地文件上传，流式上传无法做到多个分块并发上传，只能一个分块一个分块顺序上传。
     *
     * @return void
     */
    public void upload2() throws InterruptedException {
        //流式上传文件
        ObjectMetadata objectMetadata = new ObjectMetadata();
        File file = null;
        objectMetadata.setContentLength(file.length());
        InputStream inputStream = null;
        Upload upload = transferManager.upload("your-bucketname", "your-objectname", inputStream, objectMetadata);
        UploadResult result = upload.waitForUploadResult();
        System.err.println(result);

    }

    /**
     * 您可以使用TransferManager将某个目录下的文件全部上传到NOS,对象名即文件名
     * 不支持多级目录
     */
    private void test() throws InterruptedException {
        MultipleFileUpload result = transferManager.uploadDirectory("your-buckename", null, new File("dirPath"), false);
        result.waitForCompletion();
    }

    /**
     * 您可以使用TransferManager将某个目录下的文件全部上传到NOS,对象名即文件名
     * 支持多级目录，会递归的上传目录下的所有文件
     */
    private void test2() throws InterruptedException {
        MultipleFileUpload result = transferManager.uploadDirectory("your-buckename", null, new File("dirPath"), true);
        result.waitForCompletion();
    }

    /**
     * 下载文件
     */
    private void down() throws InterruptedException {
        File file = new File("your-destFile");
        Download download = transferManager.download("your-bucketname", "your-objectname", file);
        download.waitForCompletion();
    }

    /**
     * AWS SDK 公开了一个名为 TransferManager 的高级别 API，用于简化分段上传。有关更多信息，请参阅 使用分段上传来上传和复制对象。
     * <p>
     * 您可以从文件或流上传数据。您还可以设置高级选项，例如，您想要用于分段上传的分段大小或在上传分段时要使用的并发线程数。
     * 您也可以设置可选的对象属性、存储类或访问控制列表 (ACL)。您可以使用 PutObjectRequest 和 TransferManagerConfiguration 类来设置这些高级选项。
     *
     * @param keyName keyName
     * @param file    file
     * @return CompleteMultipartUploadResult
     */
//    @Override
    public String highLevelMultipartUpload(String keyName, File file) throws InterruptedException {
        String upPrefix = UUID.randomUUID().toString();
        String key = upPrefix + "/" + keyName;
        // 使用 TransferManager 上传
        TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();

        Upload upload = transferManager.upload("your-bucketname", key, file);
        upload.waitForCompletion();
//        logger.info("Object upload complete");
        return key;
    }

    public void highLevelMultipartUpload(String bucketName, String keyName, File file) throws InterruptedException {
//        String upPrefix = UUID.randomUUID().toString();
//        String key = upPrefix + "/" + keyName;
        // 使用 TransferManager 上传
        TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();
        Upload upload = transferManager.upload(bucketName, keyName, file);
        upload.waitForCompletion();
        UploadResult uploadResult = upload.waitForUploadResult();

//        logger.info("Object upload complete");
    }

    /**
     * EDS提供的分片上传(Multipart Upload)功能，将要上传的较大文件(object)分成多个分片(Part)来分别上传
     * 上传完成后再调用completeMultipartUpload接口将这些Part组合成一个object来达到断点续传的效果。
     *
     * @param keyName    keyName
     * @param bucketName yourBucketName
     * @param file       file
     * @return CompleteMultipartUploadResult
     */
//    @Override
    public CompleteMultipartUploadResult bigFileListShardingUpload(String bucketName, String keyName, File file) {
        List<PartETag> partETags = new ArrayList<>();
        long filePosition = 0;
        CompleteMultipartUploadResult result = null;
        String uploadId = null;
        InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(bucketName, keyName);
        InitiateMultipartUploadResult initResponse = amazonS3.initiateMultipartUpload(initRequest);
        uploadId = initResponse.getUploadId();
        try {
            long contentLength = file.length();
            // Set part size to 25 MB
            long partSize = 25 * 1024 * 1024;
            logger.info("-------开始只能进入分片上传阶段-------");
            for (int i = 1; filePosition < contentLength; i++) {
                partSize = Math.min(partSize, (contentLength - filePosition));
                UploadPartRequest uploadRequest = new UploadPartRequest()
                        .withBucketName(bucketName)
                        .withKey(keyName)
                        .withUploadId(uploadId)
                        .withPartNumber(i)
                        .withFileOffset(filePosition)
                        .withFile(file)
                        .withPartSize(partSize);
                UploadPartResult uploadResult = amazonS3.uploadPart(uploadRequest);
                // 返回文件的ETag值，用于验证文件是否被正确上传
                partETags.add(uploadResult.getPartETag());
                filePosition += partSize;
                logger.info("文件分片上传--->" + filePosition);
            }
            logger.info("-------所有分片上传完整------->进入分片合并阶段-------");
            // 完成分片上传
            CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(bucketName, keyName,
                    initResponse.getUploadId(), partETags);
            result = amazonS3.completeMultipartUpload(compRequest);
        } catch (SdkClientException e) {
            logger.error("分片上传错误，第{}片发生意外", filePosition, e);
            throw new RuntimeException(e);
        }
        logger.info("-------大文件分片上传完成--->" + filePosition);
        return result;
    }

    /**
     * 分片上传
     *
     * @param bucketName        桶名称
     * @param fileInputStream   文件流
     * @param fileContentLength 原始文件的length
     * @param keyName           唯一文件标识
     * @return
     */
//    @Override
    public CompleteMultipartUploadResult uploadLargeFileToS3(String bucketName, FileInputStream fileInputStream, long fileContentLength, String keyName) {
        InitiateMultipartUploadResult initResponse = null;
        InitiateMultipartUploadRequest initRequest = null;
        CompleteMultipartUploadResult completeMultipartUploadResult = null;
        try {
            // 初始化多部分上传
            initRequest = new InitiateMultipartUploadRequest(bucketName, keyName);
            initResponse = amazonS3.initiateMultipartUpload(initRequest);
            // 每个分片大小（例如：5MB）
            long partSize = 5 * 1024 * 1024;

            List<PartETag> partETags = new ArrayList<>();
            long bytePosition = 0;

            for (int i = 1; bytePosition < fileContentLength; i++) {
                long bytesRemaining = fileContentLength - bytePosition;
                partSize = Math.min(bytesRemaining, partSize);

                UploadPartRequest uploadRequest = new UploadPartRequest()
                        .withBucketName(bucketName)
                        .withKey(keyName)
                        .withUploadId(initResponse.getUploadId())
                        .withPartNumber(i)
                        .withFileOffset(bytePosition)
                        .withInputStream(fileInputStream)
                        .withPartSize(partSize);

                UploadPartResult uploadResult = amazonS3.uploadPart(uploadRequest);
                partETags.add(uploadResult.getPartETag());
                bytePosition += partSize;
            }
            // 完成多部分上传
            CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(
                    bucketName,
                    keyName,
                    initResponse.getUploadId(),
                    partETags);
            completeMultipartUploadResult = amazonS3.completeMultipartUpload(compRequest);
        } catch (SdkClientException e) {
            // 处理异常，可能需要回滚已上传的部分
            logger.error("Error uploading file to S3", e);
            // 如果有错误，尝试取消上传
            AbortMultipartUploadRequest abortMPURequest = new AbortMultipartUploadRequest(
                    bucketName,
                    keyName,
                    initResponse.getUploadId());
            amazonS3.abortMultipartUpload(abortMPURequest);
        } finally {
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return completeMultipartUploadResult;
    }


    //2、使用Delimiter模拟文件夹功能
    //
    //假设桶内有如下对象：a/1.jpg、a/2.jpg、a/b/1.txt、a/b/2.txt，列举a文件夹下的文件及子文件夹的示例代码如下：
    public void listDelimiter(String bucketName, String prefix) {

        ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
        listObjectsRequest.setBucketName(bucketName);
//        listObjectsRequest.setDelimiter("/");
        listObjectsRequest.setPrefix(prefix);
        ObjectListing listing = amazonS3.listObjects(listObjectsRequest);
        // 遍历所有Object
        System.out.println("Objects:");
        for (S3ObjectSummary objectSummary : listing.getObjectSummaries()) {
            System.out.println(objectSummary.getKey());
        }

        System.out.println("CommonPrefixs:" + listing.getPrefix());
        // 遍历所有CommonPrefix
        System.out.println("CommonPrefixs:");
        for (String commonPrefix : listing.getCommonPrefixes()) {
            System.out.println(commonPrefix);
        }

    }

}